#include "capture.h"

void Capture::Save(HWND hwnd)
{
        RECT rect;
        GetWindowRect(hwnd, &rect);

        HDC hDC = GetDC(hwnd);
        HDC hCaptureDC = CreateCompatibleDC(hDC);
        HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDC, rect.right, rect.bottom);

        SelectObject(hCaptureDC, hCaptureBitmap);
        BitBlt(hCaptureDC, 0, 0, rect.right, rect.bottom, hDC, 0, 0, SRCCOPY);

        // How big is the result image
        int width = rect.right / 2;
        int height = rect.bottom / 2;

        // HALFTONE give more quality to image
        SetStretchBltMode(hCaptureDC, HALFTONE);
        StretchBlt(hCaptureDC, 0, 0, width, height, hCaptureDC,
                0, 0, rect.right, rect.bottom, SRCCOPY);

        // Create bitmap
        BITMAP bmp;
        GetObject(hCaptureBitmap, sizeof(BITMAP), (LPSTR)&bmp);
        bmp.bmBitsPixel = 16;   // lower file size
        // Crop immediately using size
        bmp.bmWidth = width;
        bmp.bmHeight = height;

        PBITMAPINFO pbi = CreateBitmapInfoStruct(0, bmp);
        CreateBMPFile(hwnd, GenerateFileName(), pbi, hCaptureBitmap, hCaptureDC);

        DeleteObject(hCaptureBitmap);
        DeleteDC(hCaptureDC);
        ReleaseDC(hwnd, hDC);
}

void Capture::CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBmp, HDC hDC) 
{ 
        HANDLE hf;                  // file handle 
        BITMAPFILEHEADER hdr;       // bitmap file-header 
        PBITMAPINFOHEADER pbih;     // bitmap info-header 
        LPBYTE lpBits;              // memory pointer 
        DWORD dwTotal;              // total count of bytes 
        DWORD cb;                   // incremental count of bytes 
        BYTE *hp;                   // byte pointer 
        DWORD dwTmp; 

        pbih = (PBITMAPINFOHEADER)pbi; 
        lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

        if (!lpBits) 
                HandleError(_T("Error at GlobalAlloc\n")); 

        // Retrieve the color table (RGBQUAD array) and the bits 
        // (array of palette indices) from the DIB. 
        if (!GetDIBits(hDC, hBmp, 0, (WORD)pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS)) 
                HandleError(_T("Error at GetDIBits\n")); 

        // Create the .BMP file. 
        hf = CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE, (DWORD)0, 
                   NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);

        if (hf == INVALID_HANDLE_VALUE) 
                HandleError(_T("Error at CreateFile\n")); 

        hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M" 
        // Compute the size of the entire file. 
        hdr.bfSize = (DWORD)(sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed 
                     * sizeof(RGBQUAD) + pbih->biSizeImage); 
        hdr.bfReserved1 = 0; 
        hdr.bfReserved2 = 0; 

        // Compute the offset to the array of color indices. 
        hdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed 
                        * sizeof(RGBQUAD); 

        // Copy the BITMAPFILEHEADER into the .BMP file. 
        if (!WriteFile(hf, (LPVOID)&hdr, sizeof(BITMAPFILEHEADER), (LPDWORD)&dwTmp, NULL)) 
                HandleError(_T("Error at WriteFile\n")); 

        // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. 
        if (!WriteFile(hf, (LPVOID)pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof(RGBQUAD),
                (LPDWORD)&dwTmp, NULL)) 
                HandleError(_T("Error at WriteFile\n")); 

        // Copy the array of color indices into the .BMP file. 
        dwTotal = cb = pbih->biSizeImage; 
        hp = lpBits; 
        if (!WriteFile(hf, (LPSTR)hp, (int)cb, (LPDWORD)&dwTmp, NULL)) 
           HandleError(_T("Error at WriteFile\n")); 

        // Close the .BMP file. 
        if (!CloseHandle(hf)) 
           HandleError(_T("Error at CloseHandle\n")); 

        // Free memory. 
        GlobalFree((HGLOBAL)lpBits);
}

PBITMAPINFO Capture::CreateBitmapInfoStruct(HWND hwnd, BITMAP bmp)
{ 
        PBITMAPINFO pbmi;
        WORD cClrBits;

        // Convert the color format to a count of bits.
        cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);

        if (cClrBits == 1)
                cClrBits = 1;
        else if (cClrBits <= 4)
                cClrBits = 4;
        else if (cClrBits <= 8)
                cClrBits = 8;
        else if (cClrBits <= 16)
                cClrBits = 16;
        else if (cClrBits <= 24)
                cClrBits = 24;
        else
                cClrBits = 32;

        // Allocate memory for the BITMAPINFO structure. (This structure
        // contains a BITMAPINFOHEADER structure and an array of RGBQUAD
        // data structures.)
        if (cClrBits != 24)
                pbmi = (PBITMAPINFO)LocalAlloc(LPTR,
                       sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits));
        // There is no RGBQUAD array for the 24-bit-per-pixel format.
        else
                pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));

        // Initialize the fields in the BITMAPINFO structure.
        pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        pbmi->bmiHeader.biWidth = bmp.bmWidth; 
        pbmi->bmiHeader.biHeight = bmp.bmHeight; 
        pbmi->bmiHeader.biPlanes = bmp.bmPlanes; 
        pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; 
        if (cClrBits < 24) 
                pbmi->bmiHeader.biClrUsed = (1 << cClrBits); 

        // If the bitmap is not compressed, set the BI_RGB flag. 
        pbmi->bmiHeader.biCompression = BI_RGB; 

        // Compute the number of bytes in the array of color 
        // indices and store the result in biSizeImage. 
        // For Windows NT, the width must be DWORD aligned unless 
        // the bitmap is RLE compressed. This example shows this. 
        // For Windows 95/98/Me, the width must be WORD aligned unless the 
        // bitmap is RLE compressed.
        pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits + 31) & ~31) / 8
                                      * pbmi->bmiHeader.biHeight;

        // Set biClrImportant to 0, indicating that all of the 
        // device colors are important. 
        pbmi->bmiHeader.biClrImportant = 0; 
        return pbmi; 
}

LPTSTR Capture::GenerateFileName()
{
        // Generate file name based on timestamp
        SYSTEMTIME time;
        GetLocalTime(&time);

        LPTSTR buf = new TCHAR[MAX_PATH];
        SHGetSpecialFolderPath(NULL, buf, CSIDL_LOCAL_APPDATA, TRUE);

        wsprintf(buf, _T("%s\\sce\\%04d.%02d.%02d"), buf,
                time.wYear, time.wMonth, time.wDay);
        SHCreateDirectory(NULL, buf);

        wsprintf(buf, _T("%s\\%02d.%02d.%02d.bmp"), buf,
                time.wHour, time.wMinute, time.wSecond);

        //OutputDebugString(buf);
        return buf;
}

void Capture::HandleError(LPTSTR msg)
{
        OutputDebugString(msg);
}
